#!usr/bin/python
from random import choice, randrange, randint
from string import ascii_letters, digits
import sys

indent = 0
depth = 1;
global MAX_DEPTH

MAX_CASES = 5
MAX_WIDTH = 30

MAX_ID_LENGTH = 15
MAX_STRING_LENGTH = 15

class idSet:
    int  = []
    temp = []
    bool = []
    reserved = [
    'C', 'F', 'K', 'int', 'bool','true', 'false','or', 'and', 'not',
    'print', 'input', 'if', 'else', 'while', 'break', 'switch', 'case'
    ]
    def __init__(self):
        self.table={'int' : [ ['i'] ], 'temp': [ ['c'] ], 'bool': [ ['b'] ] }
    
    def assemble(self, key):
        return [item for sublist in self.table[key] for item in sublist]
    
    def CreateID(self, key):
        collect = [self.assemble(key) for key in self.table.keys()] + self.reserved 
        while True:
            id = create_string(ascii_letters, MAX_ID_LENGTH, 1);
            if id in collect: continue;
            self.table[key][-1].append(id)
            return [ [ id ] ]
        
    def enterScope(self):
        for i in self.table.values():
            i.append([])

    def exitScope(self):
        for i in self.table.values():
            i.pop()

vars = idSet()   

def create_string(chars, length, start=0):
    return ''.join([choice(chars) for _ in range(randrange(start,length)) ])

LP, RP, SC, ASSIGN  = '(', ') ', ';', '=' 
PLUS, MINUS, MULT, DIV = '+', '-', '*', '/'
CELSIUS, FAHRENHEIT, KELVIN, INT, BOOL = 'C ', 'F ', 'K ', 'int ', 'bool '
TRUE, FALSE = 'true', 'false'
OR, AND, NOT = ' or ', ' and ', 'not '
PRINT, INPUT, IF, ELSE, WHILE, BREAK, SWITCH, CASE = 'print ', 'input ', 'if ', 'else ', 'while ', 'break ', 'switch ', 'case '

def Indent():
    global indent
    indent += 1;
    return [['']]

def Unindent():
    global indent
    indent -= 1;
    return [['']]

def Indentation():
    return [ ['  ' * indent] ]

def LC():
    global vars
    Indent()
    vars.enterScope()
    return [  ['{', '\n'  ]  ]

def RC():
    Unindent()
    global vars
    vars.exitScope()
    return [  [Indentation, '}' ]  ]

def STRING():
    return [
    [ '"'+create_string(ascii_letters+digits, MAX_STRING_LENGTH)+'"']
    ]

def NUM():
    return [
    [ str(randint(0, 32564)) + ' ' ]
    ]

def REL_OP():
    return [ 
    ['>'] , ['<'], ['<='], ['>='], ['=='], ['!=']
    ]

def CreateIntID():  return vars.CreateID('int');
def CreateBoolID(): return vars.CreateID('bool')
def CreateTempID(): return vars.CreateID('temp')
        
def IntID():  return vars.assemble('int')
def BoolID(): return vars.assemble('bool')
def TempID(): return vars.assemble('temp')

def AritID():  return IntID() + TempID()

def Prog():
    return [ [ Statements ] ]
    
def Statements():
    return [
    [ Indentation, LC, Indentation, Statement,'\n', RC, '\n' ],
    [ Indentation, Statement, '\n'   ] * randrange(1,MAX_WIDTH)
    ]

def CmpdStmt():
    return [
    [ Indentation, LC, Indentation, Statement,'\n',  RC ],
    [ Indent, Indentation, Statement, Unindent ]        
    ]

loopdepth = [0]
def IncLoop(): 
    loopdepth[0] += 1
    return [ [''] ]

def DecLoop():
    loopdepth[0] -= 1
    return [ [''] ]

def Statement():
    global loopdepth
    leaves = [
    [ Assign ],
    [ IfStmt ],
    [ Declare ],
    [ INOUT ],
    ]
    deep = [
    [ WHILE, LP, BoolExp, RP, '\n', IncLoop, CmpdStmt, DecLoop ],
    [ SWITCH, LP, TempExp, RP, LC, Indent, CasesList, Unindent, RC ],
    ]
    if (loopdepth[0] > 0):
        leaves.append([ BREAK, SC ]) 
    if depth > MAX_DEPTH:
        return leaves
    return leaves+deep

def Assign():
    return [
    [ IntID,  ASSIGN, Exp, SC ],
    [ TempID, ASSIGN, TempExp, SC ],
    [ BoolID, ASSIGN, BoolExp, SC ],
    ]
    
def Declare():
    exp, texp, bexp = Exp()[0], TempExp()[0], BoolExp()[0]
    exp1, texp1, bexp1 = (lambda : [ exp ]), (lambda : [ texp ]), (lambda : [ bexp ])
    return [
    [ INT, CreateIntID, SC ],  
    [ INT, CreateIntID, ASSIGN, exp1, SC ],
    [ TempratureType, CreateTempID, SC ],
    [ TempratureType, CreateTempID, ASSIGN, texp1, SC ],
    [ BoolType, CreateBoolID, SC ],
    [ BoolType, CreateBoolID, ASSIGN, bexp1, SC ],
    ]

def INOUT():
    return [
    [ PRINT, AritID, SC ],
    [ PRINT, STRING, SC ],
    [ INPUT, AritID, SC ],
    ]

def CasesList():
    return [
    [ Indentation, CaseStat, '\n' ] * randrange(1,MAX_CASES)
    ]

def CaseStat():
    return [
    [ CASE, NUM, TempratureType, ':\n',  CmpdStmt, '\n', Indentation, BREAK, SC]
    ]
       
def IfStmt():
    return [
    [ IF, LP, BoolExp, RP, '\n', CmpdStmt ],
    [ IF, LP, BoolExp, RP, '\n', CmpdStmt, '\n', Indentation, ELSE, '\n', CmpdStmt],
    ]


def TempratureType():
    return [ [ CELSIUS ], [ FAHRENHEIT ], [ KELVIN ], ]

def BoolType():
    return [ [ BOOL ] ]
    
def Type():
    return [ [ TempratureType ], [ INT ]  ]

def Op():
    return [
    [ PLUS ],
    [ MINUS ],
    [ MULT],
    [ DIV ],
    ]

def Exp(): 
    global depth
    if depth > MAX_DEPTH:
        return [ [ IntID ], [ NUM ],]
    return [
    [ LP, Exp, RP ],
    [ IntID ],
    [ NUM ],
    [ Exp, Op, Exp ],
    [ TempExp, DIV, TempExp ],
    ]
    
def TempExp():
    global depth
    if depth > MAX_DEPTH:
        return [ [ TempID ], [ NUM, ' ', TempratureType ] ]
    return [
    [ LP, TempExp, RP ],
    [ TempID ],
    [ NUM, ' ', TempratureType ],
    [ LP, TempExp, PLUS, TempExp, RP ],
    [ LP, TempExp, MINUS, TempExp, RP ],
    [ LP, Exp, MULT, LP, TempExp, RP, RP ],
    [ LP, TempExp, MULT, LP, Exp, RP, RP ],
    [ LP, TempExp, DIV, LP, Exp, RP, RP ],
    ]   
    
def BoolExp():
    global depth
    if depth > MAX_DEPTH:
        return [ [ BoolID ], [ TRUE ], [ FALSE ], ]
    return [
    [ LP, BoolExp, RP ],
    [ BoolID ],
    [ TRUE ],
    [ FALSE ],
    [ NOT, BoolExp ],
    [ BoolExp, OR, BoolExp ],
    [ BoolExp, AND, BoolExp ],
    [ LP, Exp, REL_OP, Exp, RP ]
    ]

def derive(foo):
    global depth
    if not callable(foo):
        return str(foo)
    assert (callable(foo))
        
    depth *= 2;
    res = ''.join([ derive(i) for i in choice(foo()) ])
    depth /= 2;
    return res

if __name__ == '__main__':
    if (len(sys.argv) > 1):
        global MAX_DEPTH
        MAX_DEPTH = int(sys.argv[1])
    else:
        MAX_DEPTH = 128
    print '''int i;
bool b;
C c;
'''  + derive(Prog)
    